package com.cpz.mybatisplus.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cpz.mybatisplus.entity.User;
import com.cpz.mybatisplus.mapper.UserMapper;
import com.cpz.mybatisplus.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * @author cpz
 * @since 2022/09/21
 * 批量操作测试结果耗时：3 > 2 > 4 > 1
 * {@link ReflectiveMethodInvocation#proceed()}
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserMapper userMapper;
    private final SqlSessionFactory sqlSessionFactory;

    /**
     * 1
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void foreachInsert(List<User> users) {
        long startTime = System.currentTimeMillis();
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        for (User user : users) {
            mapper.save(user);
        }
        sqlSession.commit();
        long endTime = System.currentTimeMillis();

        log.info("单条插入总耗时 {} ms", (endTime - startTime));
    }

    /**
     * 2
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchInsert(List<User> users) {
        long startTime = System.currentTimeMillis();
        userMapper.saveBatch(users);
        long endTime = System.currentTimeMillis();
        log.info("批量插入总耗时 {} ms", (endTime - startTime));
    }


    /**
     * 3
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void foreachInsertMP(List<User> users) {
        long startTime = System.currentTimeMillis();
        for (User user : users) {
            super.save(user);
        }
        long endTime = System.currentTimeMillis();
        log.info("MP单条插入总耗时 {} ms", (endTime - startTime));
    }

    /**
     * 4
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchInsertMP(List<User> users) {
        long startTime = System.currentTimeMillis();
        super.saveBatch(users);
        long endTime = System.currentTimeMillis();
        log.info("MP批量插入总耗时 {} ms", (endTime - startTime));
    }

    @Override
    @Transactional
    public void privateMethod() {
        // 由于方法idea会检查方法访问权限修饰符private，此处就不进行测试。
    }

    @Override
    @Transactional
    public void exceptionType(boolean isEnableRollbackException) throws IOException {
        userMapper.insert(User.builder().name("cpz").age(18).email("cpz@qq.com").build());
        if (isEnableRollbackException) {
            throw new RuntimeException("RUNTIME Exception");
        } else {
            throw new IOException("IOE Exception");
        }
    }

    @Override
    public void internalMethod() {
        this.insertUser();
        throw new RuntimeException("Runtime Exception");
    }

    @Transactional
    public void insertUser() {
        userMapper.insert(User.builder().name("cpz").age(18).email("cpz@qq.com").build());
    }

    @Override
    public void multiThread(boolean isThrowFromParentThread) {
        if (isThrowFromParentThread) {
            new Thread(() -> {
                userMapper.insert(User.builder().name("cpz").age(18).email("cpz@qq.com").build());
            }).start();
            throw new RuntimeException();
        } else {
            new Thread(() -> {
                userMapper.insert(User.builder().name("cpz").age(18).email("cpz@qq.com").build());
                throw new RuntimeException();
            }).start();
        }
    }

    @Override
    public void truncate() {
        baseMapper.truncate();
    }
}
